﻿Imports DotNetNuke.Entities.Modules
Imports System.Data
Imports Aricie.DNN.Modules.LuceneSearch.Business
Imports System.Globalization
Imports Aricie.DNN.Services.Filtering
Imports System.Reflection

Public Class GridViewResultsHelper

#Region "Public Shared Methods"

    Private Const regexForTokensList As String = "(?:[\[\w:() ./'\]])+"
    Private Const regexForCustomHeader As String = "CustomHeader(?:[\(]([\w ./']+)[\)])"
    Private Const regexForCustomSort As String = "CustomSort(?:[\(]([\w ']+)[\)])"
    Private Const regexForPdfSearch As String = "CustomSearchPDF(?:[\(]([\w ']+)[\)])"

    ''' <summary>
    ''' Renvoie la Liste des Tokens Contenus dans le template
    ''' </summary>
    ''' <param name="template"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function GetAllTokens(ByVal template As String) As MatchCollection

        Dim matchCollection As MatchCollection = Regex.Matches(template, regexForTokensList, RegexOptions.IgnoreCase)

        Return matchCollection
    End Function

    ''' <summary>
    ''' Renvoie le premier champs trouvé dans le template
    ''' </summary>
    ''' <param name="template"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function GetFirstNameField(ByVal template As String) As String
        Dim toReturn As String = ""

        Dim matchCollection As MatchCollection = GetAllTokens(template)
        If matchCollection IsNot Nothing Then
            toReturn = GetNameField(matchCollection.Item(0).Value)
        End If
        Return toReturn
    End Function

    ''' <summary>
    ''' Renvoie le Nom du Champs
    ''' </summary>
    ''' <param name="field"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function GetNameField(ByVal field As String) As String
        Dim toReturn As String = field.Replace("[Result:", "").Replace("Field:", "").Replace("]", "")

        toReturn = toReturn.Replace("CustomHeader(" + GetCustomHeader(toReturn) + ")", "")
        toReturn = toReturn.Replace(":CustomType(Date)", "")
        toReturn = toReturn.Replace("HideHeader", "")
        toReturn = toReturn.Replace("HideColumn", "")
        toReturn = toReturn.Replace("CustomType(Link)", "")
        toReturn = toReturn.Replace("CustomSearchPDF(" + GetPdfSearch(toReturn) + ")", "")
        toReturn = toReturn.Replace("CustomSort(" + GetCustomSort(toReturn) + ")", "")
        toReturn = toReturn.Replace(":", "")
        Return toReturn.Trim()
    End Function

    ''' <summary>
    ''' Renvoie Vrai si le Token est une Date
    ''' </summary>
    ''' <param name="field"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function TokenIsDate(ByVal field As String) As Boolean
        Dim toReturn As Boolean = False
        If field.Contains(":CustomType(Date)") Then
            toReturn = True
        End If
        Return toReturn
    End Function

    ''' <summary>
    ''' Renvoie Vrai si le Token est un Link
    ''' </summary>
    ''' <param name="field"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function TokenIsLink(ByVal field As String) As Boolean
        Dim toReturn As Boolean = False
        If field.Contains(":CustomType(Link)") Then
            toReturn = True
        End If
        Return toReturn
    End Function

    ''' <summary>
    ''' Renvoie vrai si le Token est un champs additionnel
    ''' </summary>
    ''' <param name="field"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function TokenIsAdditionnalField(ByVal field As String) As Boolean
        Dim toReturn As Boolean = False
        If field.Contains(":Field:") Then
            toReturn = True
        End If
        Return toReturn
    End Function

    ''' <summary>
    ''' Renvoie vrai si le header de la GridVIew doit être caché
    ''' </summary>
    ''' <param name="field"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function TokenHideHeader(ByVal field As String) As Boolean
        Dim toReturn As Boolean = False
        If field.Contains(":HideHeader") Then
            toReturn = True
        End If
        Return toReturn
    End Function

    ''' <summary>
    ''' Renvoie vrai si la colonne ne doit pas être affichée
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function TokenHideColumn(ByVal field As String) As Boolean
        Dim toReturn As Boolean = False
        If field.Contains(":HideColumn") Then
            toReturn = True
        End If
        Return toReturn
    End Function

    ''' <summary>
    ''' Renvoie le CustomHeader si il y en a un
    ''' </summary>
    ''' <param name="token"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function GetCustomHeader(ByVal token As String) As String
        Dim toReturn As String = ""

        Dim matchCollection As MatchCollection = Regex.Matches(token, regexForCustomHeader, RegexOptions.IgnoreCase)
        If matchCollection.Count <> 0 Then
            toReturn = matchCollection.Item(0).Value
            toReturn = toReturn.Replace("CustomHeader(", "").Replace(")", "")
        End If

        Return toReturn
    End Function

    ''' <summary>
    ''' Renvoie le champs sur lequel effectuer le tri
    ''' </summary>
    ''' <param name="token"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function GetCustomSort(ByVal token As String) As String
        Dim toReturn As String = ""
        Dim matchCollection As MatchCollection = Regex.Matches(token, regexForCustomSort, RegexOptions.IgnoreCase)
        If matchCollection.Count <> 0 Then
            toReturn = matchCollection.Item(0).Value
            toReturn = toReturn.Replace("CustomSort(", "").Replace(")", "")
        End If
        Return toReturn
    End Function

    ''' <summary>
    ''' Renvoie le Champs sur Lequel
    ''' </summary>
    ''' <param name="token"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function GetPdfSearch(ByVal token As String) As String
        Dim toReturn As String = ""

        Dim matchCollection As MatchCollection = Regex.Matches(token, regexForPdfSearch, RegexOptions.IgnoreCase)
        If matchCollection.Count <> 0 Then
            toReturn = matchCollection.Item(0).Value
            toReturn = toReturn.Replace("CustomSearchPDF(", "").Replace(")", "")
        End If

        Return toReturn
    End Function

    ''' <summary>
    ''' Renvoie le Titre à Afficher
    ''' </summary>
    ''' <param name="columnName"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function GetHeader(ByVal columnName As String, ByVal template As String) As String
        Dim toReturn As String = ""
        For Each token As Match In GetAllTokens(template)
            If token.Value.Contains(columnName) Then
                toReturn = GetCustomHeader(token.Value)
                Exit For
            End If
        Next
        Return toReturn
    End Function

    ''' <summary>
    ''' Génère la dataTable à Afficher
    ''' </summary>
    ''' <param name="matchCollection"></param>
    ''' <param name="results"></param>
    ''' <param name="collectionColumnsDate"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function GenerateDataTable(ByVal matchCollection As MatchCollection, _
                                             ByVal results As IList(Of LuceneSearchResultsInfo), _
                                             ByRef collectionColumnsDate As List(Of Integer), _
                                             ByRef collectionColumnsHideHeader As List(Of Integer), _
                                             ByRef collectionColumnsHideColumn As List(Of Integer), _
                                             ByVal collectionColumnsLink As List(Of String), _
                                             ByVal filterQuery As String, _
                                             ByVal enableHighlight As Boolean, _
                                             ByVal matchTokenFormat As String) As DataTable
        Dim dt As New DataTable

        Dim iterator As Integer = 1

        'Création d'un tableau contenant le champs et la valeur nécessaire au highlight
        Dim hashTableHighlight As New Hashtable
        If filterQuery <> "" AndAlso enableHighlight AndAlso matchTokenFormat <> String.Empty Then
            hashTableHighlight = GenerateTableHighlight(filterQuery)
        End If

        'Création des entêtes de la DataTable
        For Each myMatch As Match In matchCollection
            Dim field As String = String.Empty
            field = GetNameField(myMatch.Value)
            If Not dt.Columns.Contains(field) Then
                If TokenIsDate(myMatch.Value) Then
                    dt.Columns.Add(New DataColumn(field, GetType(Date)))
                    collectionColumnsDate.Add(iterator)
                Else
                    dt.Columns.Add(New DataColumn(field, GetType(String)))
                End If

                'Ajout à la collection si le Header doit être masqué
                If TokenHideHeader(myMatch.Value) Then
                    collectionColumnsHideHeader.Add(iterator)
                End If

                'Ajout à la collection si la colonne doit être masqué
                If TokenHideColumn(myMatch.Value) Then
                    collectionColumnsHideColumn.Add(iterator)
                End If

                'Ajout à la collection si le match est un Link
                If TokenIsLink(myMatch.Value) Then
                    collectionColumnsLink.Add(GetNameField(myMatch.Value))
                End If

                iterator += 1
            End If
        Next

        'Parcours de chaque Résultat de la Requête Lucene 
        For Each resultInfo As LuceneSearchResultsInfo In results
            Dim dr As DataRow = dt.NewRow()

            'Pour chaque Résultat on remplie les différentes colonnes
            For Each myMatch As Match In matchCollection
                Dim field As String = String.Empty
                field = GetNameField(myMatch.Value)

                'Test si la colonne est un additionnal Field
                If TokenIsAdditionnalField(myMatch.Value) Then
                    If TokenIsDate(myMatch.Value) Then

                        If resultInfo.AdditionalFields.Get(field) <> String.Empty Then
                            Dim myDate As New DateTime
                            'Nécessaire que la date soit Indexé sous la forme d'un string car stringToDate de Lucene limité à 1970
                            Dim culture As CultureInfo = Nothing
                            If resultInfo.Culture IsNot Nothing Then
                                culture = resultInfo.Culture
                            Else
                                culture = New CultureInfo("en-US")
                            End If
                            myDate = DateTime.Parse(resultInfo.AdditionalFields.Get(field), culture)

                            dr(field) = myDate.ToShortDateString
                        Else
                            dr(field) = DBNull.Value
                        End If

                    ElseIf TokenIsLink(myMatch.Value) Then
                        Dim fieldLink As String = resultInfo.AdditionalFields.Get(field)
                        fieldLink = AddParamSearchForPdf(fieldLink, filterQuery, myMatch.Value)

                        If hashTableHighlight.Count = 0 Then
                            dr(field) = fieldLink
                        Else
                            dr(field) = HighlightWords(fieldLink, field, hashTableHighlight, matchTokenFormat)
                        End If

                    Else
                        If hashTableHighlight.Count = 0 Then
                            dr(field) = resultInfo.AdditionalFields.Get(field)
                        Else
                            Dim valueField As String = resultInfo.AdditionalFields.Get(field)
                            dr(field) = HighlightWords(valueField, field, hashTableHighlight, matchTokenFormat)
                        End If

                    End If

                    'Test si la colonne n'est pas un additionnal Field
                Else
                    Dim t As Type = resultInfo.GetType()
                    Dim propField As PropertyInfo = t.GetProperty(field)
                    If propField IsNot Nothing Then
                        dr(field) = propField.GetValue(resultInfo, Nothing)
                    End If

                End If
            Next

            dt.Rows.Add(dr)
        Next

        Return dt
    End Function

    ''' <summary>
    ''' Rajout la colonne de Link sur laquelle le tri s'effectuera
    ''' </summary>
    ''' <param name="dt"></param>
    ''' <param name="columnLink"></param>
    ''' <param name="columnToAdd"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function AddColumnLink(ByVal dt As DataTable, ByVal columnLink As String, ByVal columnToAdd As String) As DataTable
        Dim dtToReturn As DataTable = dt
        'Ajout de la colonne Sans les balises html de Lien
        dtToReturn.Columns.Add(columnToAdd, GetType(String))

        'Parcours de chaque Ligne et remplissage de la colonne ColumnToAdd
        For Each dr As DataRow In dt.Rows
            dr(columnToAdd) = RemoveBalisesLink(dr(columnLink).ToString)
        Next

        Return dtToReturn
    End Function

    ''' <summary>
    ''' Renvoie une hashtable comportant en clé le champs d'origine et en valeur le champs sur lequel il faut réélement trier
    ''' </summary>
    ''' <param name="template"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function GetHashtableSort(ByVal template As String) As Hashtable
        Dim matchCollection As MatchCollection = GridViewResultsHelper.GetAllTokens(template)
        Dim toReturn As New Hashtable
        For Each myMatch As Match In matchCollection
            Dim customSort As String = GetCustomSort(myMatch.Value)
            If customSort <> String.Empty Then
                Dim fieldToSort As String = GetNameField(myMatch.Value)
                toReturn.Add(fieldToSort, customSort)
            End If
        Next
        Return toReturn
    End Function

#End Region

#Region "Private Shared Methods"

    ''' <summary>
    ''' Enlève les balises de Html de lien
    ''' </summary>
    ''' <param name="value"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Shared Function RemoveBalisesLink(ByVal value As String) As String
        Dim toReturn As String = value

        If toReturn.Contains("<") AndAlso toReturn.Contains(">") Then
            toReturn = toReturn.Substring(toReturn.IndexOf(">") + 1)
            toReturn = toReturn.Substring(0, toReturn.IndexOf("<"))
        End If

        Return toReturn.TrimStart
    End Function

    ''' <summary>
    ''' Génère la Table contenant tout les mots à afficher et le champs correspondant
    ''' </summary>
    ''' <param name="filterQuery"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Shared Function GenerateTableHighlight(ByVal filterQuery As String) As Hashtable
        Dim hashTable As New Hashtable

        Dim matchCollection As MatchCollection
        'Dim pattern As String = "\+([\w]+:\""[\w /]+\"")"
        'Dim pattern As String = "\+[\w]+:[\w ""]+"
        Dim pattern As String = "[\w]+:[\w""]+"
        matchCollection = Regex.Matches(filterQuery, pattern, RegexOptions.IgnoreCase)
        For Each myMatch As Match In matchCollection
            Dim filter As String = myMatch.Value.Replace("+", "").Replace("/", "").Replace("""", "")
            Dim tableSplit() As String = filter.Split(CChar(":"))
            tableSplit(1) = tableSplit(1).Replace(tableSplit(0), String.Empty).Trim
            If tableSplit(1) <> String.Empty Then
                If hashTable.ContainsKey(tableSplit(0)) Then
                    hashTable.Item(tableSplit(0)) = hashTable.Item(tableSplit(0)).ToString + " " + tableSplit(1)
                Else
                    hashTable.Add(tableSplit(0), tableSplit(1))
                End If
            End If
        Next

        Return hashTable
    End Function

    ''' <summary>
    ''' Rajoute le HighLight sur les mots
    ''' </summary>
    ''' <param name="valueField"></param>
    ''' <param name="field"></param>
    ''' <param name="hashTableHighLight"></param>
    ''' <param name="matchTokenFormat"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Shared Function HighlightWords(ByVal valueField As String, ByVal field As String, ByVal hashTableHighLight As Hashtable, ByVal matchTokenFormat As String) As String
        Dim toReturn As String = valueField
        If hashTableHighLight.ContainsKey(field) Then
            Dim strwordsToFind As String = hashTableHighLight.Item(field).ToString.TrimEnd
            Dim wordsToFind() As String = strwordsToFind.Split(CChar(" "))


            Dim valuefieldCleanOld As String = RemoveBalisesLink(valueField)
            Dim valueFieldCleanNew As String = valuefieldCleanOld

            Dim format As String() = matchTokenFormat.Split(New String() {"[Content]"}, StringSplitOptions.RemoveEmptyEntries)

            For i As Integer = 0 To wordsToFind.Length - 1
                valueFieldCleanNew = HighlightValue(valueFieldCleanNew, wordsToFind(i), format)

                Dim filter As New ExpressionFilterInfo
                filter.BuildDefault(DefaultTransforms.UrlFull)
                Dim escaper As New StringEscaper(filter)
                Dim wordEscape As String = escaper.EscapeString(wordsToFind(i))

                If wordsToFind(i) <> wordEscape Then
                    valueFieldCleanNew = HighlightValue(valueFieldCleanNew, wordEscape, format)
                End If

            Next

            toReturn = valueField.Replace(valuefieldCleanOld, valueFieldCleanNew)
        End If

        Return toReturn
    End Function

    ''' <summary>
    ''' Effectue le HighLight sur un mot
    ''' </summary>
    ''' <param name="fieldValue"></param>
    ''' <param name="wordToFind"></param>
    ''' <param name="format"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Shared Function HighlightValue(ByVal fieldValue As String, ByVal wordToFind As String, ByVal format As String()) As String
        Dim toReturn As String = fieldValue
        If wordToFind <> String.Empty Then
            Dim index As Integer = toReturn.ToLower.IndexOf(wordToFind.ToLower)

            Do While index <> -1

                If index = 0 Then
                    toReturn = toReturn.Insert(index + wordToFind.Length, format(1))
                    toReturn = toReturn.Insert(index, format(0))
                    index = toReturn.ToLower.IndexOf(wordToFind.ToLower, index + wordToFind.Length + format(0).Length + format(1).Length)

                ElseIf index + wordToFind.Length = toReturn.Length Then
                    toReturn = toReturn.Insert(index + wordToFind.Length, format(1))
                    toReturn = toReturn.Insert(index, format(0))
                    index = toReturn.ToLower.IndexOf(wordToFind.ToLower, index + wordToFind.Length + format(0).Length + format(1).Length)

                ElseIf toReturn.Chars(index - 1) = " " AndAlso toReturn.Chars(index + wordToFind.Length) = " " Then
                    toReturn = toReturn.Insert(index + wordToFind.Length, format(1))
                    toReturn = toReturn.Insert(index, format(0))
                    index = toReturn.ToLower.IndexOf(wordToFind.ToLower, index + wordToFind.Length + format(0).Length + format(1).Length)

                Else
                    index = toReturn.ToLower.IndexOf(wordToFind.ToLower, index + wordToFind.Length)
                End If

            Loop


        End If

        Return toReturn
    End Function

    ''' <summary>
    ''' Renvoie le lien avec la recherche des mots Clés
    ''' </summary>
    ''' <param name="fieldLink"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Shared Function AddParamSearchForPdf(ByVal fieldLink As String, ByVal filterQuery As String, ByVal matchToken As String) As String
        Dim toReturn As String = fieldLink
        Dim oldHref As String = String.Empty
        Dim newHref As String = String.Empty
        Dim addSearch As String = String.Empty

        Dim pdfFieldIndexed As String = GetPdfSearch(matchToken)
        If pdfFieldIndexed <> String.Empty AndAlso fieldLink <> String.Empty AndAlso filterQuery <> String.Empty Then
            'Récupérer le href
            oldHref = GetHref(fieldLink)
            If oldHref <> "" Then
                addSearch = GetStrPDFSearch(filterQuery, pdfFieldIndexed)
                If addSearch <> "" Then
                    addSearch = "#search=" + addSearch + ""
                End If
                newHref = oldHref + addSearch
                toReturn = toReturn.Replace(oldHref, newHref)
            End If
        End If
        Return toReturn
    End Function

    ''' <summary>
    ''' Renvoie la string à rajouter pour une recherchePDF
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function GetStrPDFSearch(ByVal filterQuery As String, ByVal pdfFieldIndex As String) As String

        Dim toReturn As String = String.Empty
        Dim matchCollection As MatchCollection
        Dim pattern As String = pdfFieldIndex + ":(?:[""]{0,})([\w]+)(?:[""]{0,})" '":""([\w]+)"""
        matchCollection = Regex.Matches(filterQuery, pattern, RegexOptions.IgnoreCase)
        If matchCollection IsNot Nothing Then
            For i As Integer = 0 To matchCollection.Count - 1
                toReturn += matchCollection.Item(i).Value.Replace(pdfFieldIndex + ":", "").Replace("""", "") + " "
            Next
        End If
        Return toReturn.TrimEnd

    End Function

    ''' <summary>
    ''' Renvoie le href d'un lien
    ''' </summary>
    ''' <param name="value"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Shared Function GetHref(ByVal value As String) As String
        Dim toReturn As String = String.Empty

        Dim matchCollection As MatchCollection
        Dim pattern As String = "href='([\w:/.\-?=&$+,/:;=?@#. <>%{}|\\^\[\]~`*]+)'"
        matchCollection = Regex.Matches(value, pattern, RegexOptions.IgnoreCase)
        If matchCollection IsNot Nothing AndAlso matchCollection.Count > 0 Then
            toReturn = matchCollection.Item(0).Groups(1).Value
        End If
        Return toReturn
    End Function

#End Region

End Class